<<<<<<< HEAD Customer Segmentation using R

cONNECT TO AWS S3

Authenticate With aws.s3

need to install the aws.s3 package.

install.packages("aws.s3")

Load Library

library(data.table)
data.table 1.12.8 using 2 threads (see ?getDTthreads).  Latest news: r-datatable.com

Here are , myaccesskey : AKIASLJSTH7SVRFIDUGC mysecretkey : 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

In order to connect to S3, need to authenticate. set environment variables like this

bucketlist()
Error in bucketlist() : could not find function "bucketlist"
#bucketlist()

Two errors I stumbled upon here were

Forbidden (HTTP 403) because I inserted the wrong credentials for my user Forbidden (HTTP 403) because I incorrectly set up my user’s permission to access S3

Alternatively, you can pass your access key and secret access key as parameters to aws.s3 functions directly. For example, you can make calls like this.

Read And Write Data From/To S3

Write :

Let’s start by uploading a couple CSV files to S3.

# Save customer segmentation datasets to CSV files in tempdir()

write.csv(customersegmentation_data, file.path(tempdir(), "data.csv"))
# Upload files to S3 bucket
put_object(
  file = file.path(tempdir(), "data.csv"), 
  object = "data.csv", 
  bucket = "customersegmentation_data"
)

If you get an error like 301 Moved Permanently, it most likely means that something’s gone wrong with regards to your region. It could be that

You’ve misspelled or inserted the wrong region name for the environment variable AWS_DEFAULT_REGION (if you’re using environment vars) You’ve misspelled or inserted the wrong region name for the region parameter of put_object() (if you aren’t using environment vars) You’ve incorrectly set up your user’s permissions These files are pretty small, so uploading them is a breeze. If you’re trying to upload a big file (> 100MB), you may want to set multipart = TRUE within the put_object() function.

Now let’s list all the objects in our bucket using get_bucket().

get_bucket(bucket = "customersegmentationdata")
Bucket: customersegmentationdata 

$Contents
Key:            data.csv 
LastModified:   2020-12-13T13:23:49.000Z 
ETag:           "7bd2101a21bda2d6ea265d1981d2be87-3" 
Size (B):       45580638 
Owner:          babarneeta38 
Storage class:  STANDARD 

This returns a list of s3_objects. I like using the rbindlist() function from the data.table package to collapse these objects into a nice and clean data.table (i.e. data.frame).

#data.table::rbindlist(get_bucket(bucket = "customersegentationdata"))

We can load one of these CSV files from S3 into R with the s3read_using() function. Here are three different ways to do this..

fetching and Storing data.csv into temp file

Note that the second and third examples use the object’s URI which is given as s3:///.Alternatively, you could download these files with save_object() and then read them from disc

tempfile <- tempfile()  # temp filepath like 
save_object(object = "s3://customersegmentationdata/data.csv", file = tempfile)
[1] "C:\\Users\\Irshad\\AppData\\Local\\Temp\\RtmpqKmPy8\\file2830255a24b7"
read.csv(tempfile)
NA

basic checks :

typeof(df)
[1] "list"

Get the structure and summary of the data frame.

typeof(df)
[1] "list"
str(df)
'data.frame':   541909 obs. of  8 variables:
 $ InvoiceNo  : Factor w/ 25900 levels "536365","536366",..: 1 1 1 1 1 1 1 2 2 3 ...
 $ StockCode  : Factor w/ 4070 levels "10002","10080",..: 3538 2795 3045 2986 2985 1663 801 1548 1547 3306 ...
 $ Description: Factor w/ 4224 levels ""," 4 PURPLE FLOCK DINNER CANDLES",..: 4027 4035 932 1959 2980 3235 1573 1698 1695 259 ...
 $ Quantity   : int  6 6 8 6 6 2 6 6 6 32 ...
 $ InvoiceDate: Factor w/ 23260 levels "1/10/2011 10:04",..: 6839 6839 6839 6839 6839 6839 6839 6840 6840 6841 ...
 $ UnitPrice  : num  2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
 $ CustomerID : int  17850 17850 17850 17850 17850 17850 17850 17850 17850 13047 ...
 $ Country    : Factor w/ 38 levels "Australia","Austria",..: 36 36 36 36 36 36 36 36 36 36 ...
summary(df)
   InvoiceNo        StockCode                                  Description    
 573585 :  1114   85123A :  2313   WHITE HANGING HEART T-LIGHT HOLDER:  2369  
 581219 :   749   22423  :  2203   REGENCY CAKESTAND 3 TIER          :  2200  
 581492 :   731   85099B :  2159   JUMBO BAG RED RETROSPOT           :  2159  
 580729 :   721   47566  :  1727   PARTY BUNTING                     :  1727  
 558475 :   705   20725  :  1639   LUNCH BAG RED RETROSPOT           :  1638  
 579777 :   687   84879  :  1502   ASSORTED COLOUR BIRD ORNAMENT     :  1501  
 (Other):537202   (Other):530366   (Other)                           :530315  
    Quantity                   InvoiceDate       UnitPrice           CustomerID    
 Min.   :-80995.00   10/31/2011 14:41:  1114   Min.   :-11062.06   Min.   :12346   
 1st Qu.:     1.00   12/8/2011 9:28  :   749   1st Qu.:     1.25   1st Qu.:13953   
 Median :     3.00   12/9/2011 10:03 :   731   Median :     2.08   Median :15152   
 Mean   :     9.55   12/5/2011 17:24 :   721   Mean   :     4.61   Mean   :15288   
 3rd Qu.:    10.00   6/29/2011 15:58 :   705   3rd Qu.:     4.13   3rd Qu.:16791   
 Max.   : 80995.00   11/30/2011 15:13:   687   Max.   : 38970.00   Max.   :18287   
                     (Other)         :537202                       NA's   :135080  
           Country      
 United Kingdom:495478  
 Germany       :  9495  
 France        :  8557  
 EIRE          :  8196  
 Spain         :  2533  
 Netherlands   :  2371  
 (Other)       : 15279  

Quantity is greater than 10

LS0tDQp0aXRsZTogIkN1c3RvbWVyIFNlZ21lbnRhdGlvbiB1c2luZyBSIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBjT05ORUNUIFRPIEFXUyBTMw0KDQojIyBBdXRoZW50aWNhdGUgV2l0aCBhd3MuczMNCm5lZWQgdG8gaW5zdGFsbCB0aGUgYXdzLnMzIHBhY2thZ2UuDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImF3cy5zMyIpDQpgYGANCg0KTG9hZCBMaWJyYXJ5DQpgYGB7cn0NCmxpYnJhcnkoYXdzLnMzKQ0KYGBgDQoNCkhlcmUgYXJlICAsDQpteWFjY2Vzc2tleSA6IEFLSUFTTEpTVEg3U1ZSRklEVUdDDQpteXNlY3JldGtleSA6IEMxTEQ5QkRxbTdLZHBkR2g1UTQvOGdTTXdEUzllZkZRSVZCNXNnSWoNCg0KDQpJbiBvcmRlciB0byBjb25uZWN0IHRvIFMzLCBuZWVkIHRvIGF1dGhlbnRpY2F0ZS4NCnNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgbGlrZSB0aGlzDQpgYGB7cn0NClN5cy5zZXRlbnYoDQogICJBV1NfQUNDRVNTX0tFWV9JRCIgPSAiQUtJQVNMSlNUSDdTVlJGSURVR0MiLA0KICAiQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZIiA9ICJDMUxEOUJEcW03S2RwZEdoNVE0LzhnU013RFM5ZWZGUUlWQjVzZ0lqIiwNCiAiQVdTX0RFRkFVTFRfUkVHSU9OIiA9ICJhcC1zb3V0aGVhc3QtMSINCiAgICMidXMtZWFzdC0xIg0KICAgIyJhcC1zb3V0aGVhc3QtMSINCikNCg0KYGBgDQoNCmBgYHtyfQ0KI2J1Y2tldGxpc3QoKQ0KYGBgDQoNClR3byBlcnJvcnMgSSBzdHVtYmxlZCB1cG9uIGhlcmUgd2VyZQ0KDQpGb3JiaWRkZW4gKEhUVFAgNDAzKSBiZWNhdXNlIEkgaW5zZXJ0ZWQgdGhlIHdyb25nIGNyZWRlbnRpYWxzIGZvciBteSB1c2VyDQpGb3JiaWRkZW4gKEhUVFAgNDAzKSBiZWNhdXNlIEkgaW5jb3JyZWN0bHkgc2V0IHVwIG15IHVzZXLigJlzIHBlcm1pc3Npb24gdG8gYWNjZXNzIFMzDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gcGFzcyB5b3VyIGFjY2VzcyBrZXkgYW5kIHNlY3JldCBhY2Nlc3Mga2V5IGFzIHBhcmFtZXRlcnMgdG8gYXdzLnMzIGZ1bmN0aW9ucyBkaXJlY3RseS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gbWFrZSBjYWxscyBsaWtlIHRoaXMuDQoNCmBgYHtyfQ0KYnVja2V0bGlzdChrZXkgPSAiQUtJQVNMSlNUSDdTVlJGSURVR0MiLCBzZWNyZXQgPSAiQzFMRDlCRHFtN0tkcGRHaDVRNC84Z1NNd0RTOWVmRlFJVkI1c2dJaiIpDQpgYGANCg0KIyBSZWFkIEFuZCBXcml0ZSBEYXRhIEZyb20vVG8gUzMNCg0KIyBXcml0ZSA6DQoNCkxldOKAmXMgc3RhcnQgYnkgdXBsb2FkaW5nIGEgY291cGxlIENTViBmaWxlcyB0byBTMy4NCg0KYGBge3J9DQojIFNhdmUgY3VzdG9tZXIgc2VnbWVudGF0aW9uIGRhdGFzZXRzIHRvIENTViBmaWxlcyBpbiB0ZW1wZGlyKCkNCg0Kd3JpdGUuY3N2KGN1c3RvbWVyc2VnbWVudGF0aW9uX2RhdGEsIGZpbGUucGF0aCh0ZW1wZGlyKCksICJkYXRhLmNzdiIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBVcGxvYWQgZmlsZXMgdG8gUzMgYnVja2V0DQpwdXRfb2JqZWN0KA0KICBmaWxlID0gZmlsZS5wYXRoKHRlbXBkaXIoKSwgImRhdGEuY3N2IiksIA0KICBvYmplY3QgPSAiZGF0YS5jc3YiLCANCiAgYnVja2V0ID0gImN1c3RvbWVyc2VnbWVudGF0aW9uX2RhdGEiDQopDQpgYGANCg0KSWYgeW91IGdldCBhbiBlcnJvciBsaWtlIDMwMSBNb3ZlZCBQZXJtYW5lbnRseSwgaXQgbW9zdCBsaWtlbHkgbWVhbnMgdGhhdCBzb21ldGhpbmfigJlzIGdvbmUgd3Jvbmcgd2l0aCByZWdhcmRzIHRvIHlvdXIgcmVnaW9uLiBJdCBjb3VsZCBiZSB0aGF0DQoNCllvdeKAmXZlIG1pc3NwZWxsZWQgb3IgaW5zZXJ0ZWQgdGhlIHdyb25nIHJlZ2lvbiBuYW1lIGZvciB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGUgQVdTX0RFRkFVTFRfUkVHSU9OIChpZiB5b3XigJlyZSB1c2luZyBlbnZpcm9ubWVudCB2YXJzKQ0KWW914oCZdmUgbWlzc3BlbGxlZCBvciBpbnNlcnRlZCB0aGUgd3JvbmcgcmVnaW9uIG5hbWUgZm9yIHRoZSByZWdpb24gcGFyYW1ldGVyIG9mIHB1dF9vYmplY3QoKSAoaWYgeW91IGFyZW7igJl0IHVzaW5nIGVudmlyb25tZW50IHZhcnMpDQpZb3XigJl2ZSBpbmNvcnJlY3RseSBzZXQgdXAgeW91ciB1c2Vy4oCZcyBwZXJtaXNzaW9ucw0KVGhlc2UgZmlsZXMgYXJlIHByZXR0eSBzbWFsbCwgc28gdXBsb2FkaW5nIHRoZW0gaXMgYSBicmVlemUuIElmIHlvdeKAmXJlIHRyeWluZyB0byB1cGxvYWQgYSBiaWcgZmlsZSAoPiAxMDBNQiksIHlvdSBtYXkgd2FudCB0byBzZXQgbXVsdGlwYXJ0ID0gVFJVRSB3aXRoaW4gdGhlIHB1dF9vYmplY3QoKSBmdW5jdGlvbi4NCg0KTm93IGxldOKAmXMgbGlzdCBhbGwgdGhlIG9iamVjdHMgaW4gb3VyIGJ1Y2tldCB1c2luZyBnZXRfYnVja2V0KCkuDQoNCmBgYHtyfQ0KZ2V0X2J1Y2tldChidWNrZXQgPSAiY3VzdG9tZXJzZWdtZW50YXRpb25kYXRhIikNCmBgYA0KDQpUaGlzIHJldHVybnMgYSBsaXN0IG9mIHMzX29iamVjdHMuIEkgbGlrZSB1c2luZyB0aGUgcmJpbmRsaXN0KCkgZnVuY3Rpb24gZnJvbSB0aGUgZGF0YS50YWJsZSBwYWNrYWdlIHRvIGNvbGxhcHNlIHRoZXNlIG9iamVjdHMgaW50byBhIG5pY2UgYW5kIGNsZWFuIGRhdGEudGFibGUgKGkuZS4gZGF0YS5mcmFtZSkuDQoNCmBgYHtyfQ0KI2RhdGEudGFibGU6OnJiaW5kbGlzdChnZXRfYnVja2V0KGJ1Y2tldCA9ICJjdXN0b21lcnNlZ2VudGF0aW9uZGF0YSIpKQ0KYGBgDQoNCioqV2UgY2FuIGxvYWQgb25lIG9mIHRoZXNlIENTViBmaWxlcyBmcm9tIFMzIGludG8gUiB3aXRoIHRoZSBzM3JlYWRfdXNpbmcoKSBmdW5jdGlvbi4gSGVyZSBhcmUgdGhyZWUgZGlmZmVyZW50IHdheXMgdG8gZG8gdGhpcy4uKioNCg0KDQpgYGB7cn0NCmRhdGEgPSBzM3JlYWRfdXNpbmcoRlVOID0gcmVhZC5jc3YsIG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIpICMgdXNlIHRoZSBzMyBVUkkNCg0KI3MzcmVhZF91c2luZyhGVU4gPSByZWFkLmNzdiwgYnVja2V0ID0gImN1c3RvbWVyc2VnbWVudGF0aW9uZGF0YSIsIG9iamVjdCA9ICJkYXRhLmNzdiIpICAgICMgYnVja2V0IGFuZCBvYmplY3Qgc3BlY2lmaWVkIHNlcGFyYXRlbHkNCiNzM3JlYWRfdXNpbmcoRlVOID0gZGF0YS50YWJsZTo6ZnJlYWQsIG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIpICAjIHVzZSBkYXRhLnRhYmxlJ3MgZnJlYWQoKSBmdW5jdGlvbiBmb3IgZmFzdCBDU1YgcmVhZGluZw0KDQpgYGANCg0KYGBge3J9DQpkYXRhDQpgYGANCg0KIyBmZXRjaGluZyBhbmQgU3RvcmluZyBkYXRhLmNzdiBpbnRvIHRlbXAgZmlsZSANCioqTm90ZSB0aGF0IHRoZSBzZWNvbmQgYW5kIHRoaXJkIGV4YW1wbGVzIHVzZSB0aGUgb2JqZWN04oCZcyBVUkkgd2hpY2ggaXMgZ2l2ZW4gYXMgczM6Ly88YnVja2V0LW5hbWU+LzxrZXk+LkFsdGVybmF0aXZlbHksIHlvdSBjb3VsZCBkb3dubG9hZCB0aGVzZSBmaWxlcyB3aXRoIHNhdmVfb2JqZWN0KCkgYW5kIHRoZW4gcmVhZCB0aGVtIGZyb20gZGlzYyoqDQoNCmBgYHtyfQ0KI3RlbXBmaWxlIDwtIHRlbXBmaWxlKCkgICMgdGVtcCBmaWxlcGF0aCBsaWtlIA0KI3NhdmVfb2JqZWN0KG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIsIGZpbGUgPSB0ZW1wZmlsZSkNCiNyZWFkLmNzdih0ZW1wZmlsZSkNCg0KYGBgDQoNCiMgYmFzaWMgY2hlY2tzIDogDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZShkYXRhKQ0KYGBgDQpHZXQgdGhlIHN0cnVjdHVyZSAgYW5kIHN1bW1hcnkgb2YgdGhlIGRhdGEgZnJhbWUuDQpgYGB7cn0NCnR5cGVvZihkZikNCnN0cihkZikNCnN1bW1hcnkoZGYpDQpgYGANCg0KUXVhbnRpdHkgaXMgZ3JlYXRlciB0aGFuIDEwIA0KDQpgYGB7cn0NCmRmW2RmJFF1YW50aXR5ID4xMCxdDQpgYGANCg0K
======= Customer Segmentation using R

cONNECT TO AWS S3

Authenticate With aws.s3

need to install the aws.s3 package.

install.packages("aws.s3")

Load Library

library(data.table)
data.table 1.12.8 using 2 threads (see ?getDTthreads).  Latest news: r-datatable.com

Here are , myaccesskey : AKIASLJSTH7SVRFIDUGC mysecretkey : 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

In order to connect to S3, need to authenticate. set environment variables like this

bucketlist()
Error in bucketlist() : could not find function "bucketlist"
#bucketlist()

Two errors I stumbled upon here were

Forbidden (HTTP 403) because I inserted the wrong credentials for my user Forbidden (HTTP 403) because I incorrectly set up my user’s permission to access S3

Alternatively, you can pass your access key and secret access key as parameters to aws.s3 functions directly. For example, you can make calls like this.

Read And Write Data From/To S3

Write :

Let’s start by uploading a couple CSV files to S3.

# Save customer segmentation datasets to CSV files in tempdir()

write.csv(customersegmentation_data, file.path(tempdir(), "data.csv"))
# Upload files to S3 bucket
put_object(
  file = file.path(tempdir(), "data.csv"), 
  object = "data.csv", 
  bucket = "customersegmentation_data"
)

If you get an error like 301 Moved Permanently, it most likely means that something’s gone wrong with regards to your region. It could be that

You’ve misspelled or inserted the wrong region name for the environment variable AWS_DEFAULT_REGION (if you’re using environment vars) You’ve misspelled or inserted the wrong region name for the region parameter of put_object() (if you aren’t using environment vars) You’ve incorrectly set up your user’s permissions These files are pretty small, so uploading them is a breeze. If you’re trying to upload a big file (> 100MB), you may want to set multipart = TRUE within the put_object() function.

Now let’s list all the objects in our bucket using get_bucket().

get_bucket(bucket = "customersegmentationdata")
Bucket: customersegmentationdata 

$Contents
Key:            data.csv 
LastModified:   2020-12-13T13:23:49.000Z 
ETag:           "7bd2101a21bda2d6ea265d1981d2be87-3" 
Size (B):       45580638 
Owner:          babarneeta38 
Storage class:  STANDARD 

This returns a list of s3_objects. I like using the rbindlist() function from the data.table package to collapse these objects into a nice and clean data.table (i.e. data.frame).

#data.table::rbindlist(get_bucket(bucket = "customersegentationdata"))

We can load one of these CSV files from S3 into R with the s3read_using() function. Here are three different ways to do this..

fetching and Storing data.csv into temp file

Note that the second and third examples use the object’s URI which is given as s3:///.Alternatively, you could download these files with save_object() and then read them from disc

tempfile <- tempfile()  # temp filepath like 
save_object(object = "s3://customersegmentationdata/data.csv", file = tempfile)
[1] "C:\\Users\\Irshad\\AppData\\Local\\Temp\\RtmpqKmPy8\\file2830255a24b7"
read.csv(tempfile)
NA

basic checks :

typeof(df)
[1] "list"

Get the structure and summary of the data frame.

typeof(df)
[1] "list"
str(df)
'data.frame':   541909 obs. of  8 variables:
 $ InvoiceNo  : Factor w/ 25900 levels "536365","536366",..: 1 1 1 1 1 1 1 2 2 3 ...
 $ StockCode  : Factor w/ 4070 levels "10002","10080",..: 3538 2795 3045 2986 2985 1663 801 1548 1547 3306 ...
 $ Description: Factor w/ 4224 levels ""," 4 PURPLE FLOCK DINNER CANDLES",..: 4027 4035 932 1959 2980 3235 1573 1698 1695 259 ...
 $ Quantity   : int  6 6 8 6 6 2 6 6 6 32 ...
 $ InvoiceDate: Factor w/ 23260 levels "1/10/2011 10:04",..: 6839 6839 6839 6839 6839 6839 6839 6840 6840 6841 ...
 $ UnitPrice  : num  2.55 3.39 2.75 3.39 3.39 7.65 4.25 1.85 1.85 1.69 ...
 $ CustomerID : int  17850 17850 17850 17850 17850 17850 17850 17850 17850 13047 ...
 $ Country    : Factor w/ 38 levels "Australia","Austria",..: 36 36 36 36 36 36 36 36 36 36 ...
summary(df)
   InvoiceNo        StockCode                                  Description    
 573585 :  1114   85123A :  2313   WHITE HANGING HEART T-LIGHT HOLDER:  2369  
 581219 :   749   22423  :  2203   REGENCY CAKESTAND 3 TIER          :  2200  
 581492 :   731   85099B :  2159   JUMBO BAG RED RETROSPOT           :  2159  
 580729 :   721   47566  :  1727   PARTY BUNTING                     :  1727  
 558475 :   705   20725  :  1639   LUNCH BAG RED RETROSPOT           :  1638  
 579777 :   687   84879  :  1502   ASSORTED COLOUR BIRD ORNAMENT     :  1501  
 (Other):537202   (Other):530366   (Other)                           :530315  
    Quantity                   InvoiceDate       UnitPrice           CustomerID    
 Min.   :-80995.00   10/31/2011 14:41:  1114   Min.   :-11062.06   Min.   :12346   
 1st Qu.:     1.00   12/8/2011 9:28  :   749   1st Qu.:     1.25   1st Qu.:13953   
 Median :     3.00   12/9/2011 10:03 :   731   Median :     2.08   Median :15152   
 Mean   :     9.55   12/5/2011 17:24 :   721   Mean   :     4.61   Mean   :15288   
 3rd Qu.:    10.00   6/29/2011 15:58 :   705   3rd Qu.:     4.13   3rd Qu.:16791   
 Max.   : 80995.00   11/30/2011 15:13:   687   Max.   : 38970.00   Max.   :18287   
                     (Other)         :537202                       NA's   :135080  
           Country      
 United Kingdom:495478  
 Germany       :  9495  
 France        :  8557  
 EIRE          :  8196  
 Spain         :  2533  
 Netherlands   :  2371  
 (Other)       : 15279  

Quantity is greater than 10

LS0tDQp0aXRsZTogIkN1c3RvbWVyIFNlZ21lbnRhdGlvbiB1c2luZyBSIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KIyBjT05ORUNUIFRPIEFXUyBTMw0KDQojIyBBdXRoZW50aWNhdGUgV2l0aCBhd3MuczMNCm5lZWQgdG8gaW5zdGFsbCB0aGUgYXdzLnMzIHBhY2thZ2UuDQpgYGB7cn0NCmluc3RhbGwucGFja2FnZXMoImF3cy5zMyIpDQpgYGANCg0KTG9hZCBMaWJyYXJ5DQpgYGB7cn0NCmxpYnJhcnkoYXdzLnMzKQ0KYGBgDQoNCkhlcmUgYXJlICAsDQpteWFjY2Vzc2tleSA6IEFLSUFTTEpTVEg3U1ZSRklEVUdDDQpteXNlY3JldGtleSA6IEMxTEQ5QkRxbTdLZHBkR2g1UTQvOGdTTXdEUzllZkZRSVZCNXNnSWoNCg0KDQpJbiBvcmRlciB0byBjb25uZWN0IHRvIFMzLCBuZWVkIHRvIGF1dGhlbnRpY2F0ZS4NCnNldCBlbnZpcm9ubWVudCB2YXJpYWJsZXMgbGlrZSB0aGlzDQpgYGB7cn0NClN5cy5zZXRlbnYoDQogICJBV1NfQUNDRVNTX0tFWV9JRCIgPSAiQUtJQVNMSlNUSDdTVlJGSURVR0MiLA0KICAiQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZIiA9ICJDMUxEOUJEcW03S2RwZEdoNVE0LzhnU013RFM5ZWZGUUlWQjVzZ0lqIiwNCiAiQVdTX0RFRkFVTFRfUkVHSU9OIiA9ICJhcC1zb3V0aGVhc3QtMSINCiAgICMidXMtZWFzdC0xIg0KICAgIyJhcC1zb3V0aGVhc3QtMSINCikNCg0KYGBgDQoNCmBgYHtyfQ0KI2J1Y2tldGxpc3QoKQ0KYGBgDQoNClR3byBlcnJvcnMgSSBzdHVtYmxlZCB1cG9uIGhlcmUgd2VyZQ0KDQpGb3JiaWRkZW4gKEhUVFAgNDAzKSBiZWNhdXNlIEkgaW5zZXJ0ZWQgdGhlIHdyb25nIGNyZWRlbnRpYWxzIGZvciBteSB1c2VyDQpGb3JiaWRkZW4gKEhUVFAgNDAzKSBiZWNhdXNlIEkgaW5jb3JyZWN0bHkgc2V0IHVwIG15IHVzZXLigJlzIHBlcm1pc3Npb24gdG8gYWNjZXNzIFMzDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gcGFzcyB5b3VyIGFjY2VzcyBrZXkgYW5kIHNlY3JldCBhY2Nlc3Mga2V5IGFzIHBhcmFtZXRlcnMgdG8gYXdzLnMzIGZ1bmN0aW9ucyBkaXJlY3RseS4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gbWFrZSBjYWxscyBsaWtlIHRoaXMuDQoNCmBgYHtyfQ0KYnVja2V0bGlzdChrZXkgPSAiQUtJQVNMSlNUSDdTVlJGSURVR0MiLCBzZWNyZXQgPSAiQzFMRDlCRHFtN0tkcGRHaDVRNC84Z1NNd0RTOWVmRlFJVkI1c2dJaiIpDQpgYGANCg0KIyBSZWFkIEFuZCBXcml0ZSBEYXRhIEZyb20vVG8gUzMNCg0KIyBXcml0ZSA6DQoNCkxldOKAmXMgc3RhcnQgYnkgdXBsb2FkaW5nIGEgY291cGxlIENTViBmaWxlcyB0byBTMy4NCg0KYGBge3J9DQojIFNhdmUgY3VzdG9tZXIgc2VnbWVudGF0aW9uIGRhdGFzZXRzIHRvIENTViBmaWxlcyBpbiB0ZW1wZGlyKCkNCg0Kd3JpdGUuY3N2KGN1c3RvbWVyc2VnbWVudGF0aW9uX2RhdGEsIGZpbGUucGF0aCh0ZW1wZGlyKCksICJkYXRhLmNzdiIpKQ0KYGBgDQoNCmBgYHtyfQ0KIyBVcGxvYWQgZmlsZXMgdG8gUzMgYnVja2V0DQpwdXRfb2JqZWN0KA0KICBmaWxlID0gZmlsZS5wYXRoKHRlbXBkaXIoKSwgImRhdGEuY3N2IiksIA0KICBvYmplY3QgPSAiZGF0YS5jc3YiLCANCiAgYnVja2V0ID0gImN1c3RvbWVyc2VnbWVudGF0aW9uX2RhdGEiDQopDQpgYGANCg0KSWYgeW91IGdldCBhbiBlcnJvciBsaWtlIDMwMSBNb3ZlZCBQZXJtYW5lbnRseSwgaXQgbW9zdCBsaWtlbHkgbWVhbnMgdGhhdCBzb21ldGhpbmfigJlzIGdvbmUgd3Jvbmcgd2l0aCByZWdhcmRzIHRvIHlvdXIgcmVnaW9uLiBJdCBjb3VsZCBiZSB0aGF0DQoNCllvdeKAmXZlIG1pc3NwZWxsZWQgb3IgaW5zZXJ0ZWQgdGhlIHdyb25nIHJlZ2lvbiBuYW1lIGZvciB0aGUgZW52aXJvbm1lbnQgdmFyaWFibGUgQVdTX0RFRkFVTFRfUkVHSU9OIChpZiB5b3XigJlyZSB1c2luZyBlbnZpcm9ubWVudCB2YXJzKQ0KWW914oCZdmUgbWlzc3BlbGxlZCBvciBpbnNlcnRlZCB0aGUgd3JvbmcgcmVnaW9uIG5hbWUgZm9yIHRoZSByZWdpb24gcGFyYW1ldGVyIG9mIHB1dF9vYmplY3QoKSAoaWYgeW91IGFyZW7igJl0IHVzaW5nIGVudmlyb25tZW50IHZhcnMpDQpZb3XigJl2ZSBpbmNvcnJlY3RseSBzZXQgdXAgeW91ciB1c2Vy4oCZcyBwZXJtaXNzaW9ucw0KVGhlc2UgZmlsZXMgYXJlIHByZXR0eSBzbWFsbCwgc28gdXBsb2FkaW5nIHRoZW0gaXMgYSBicmVlemUuIElmIHlvdeKAmXJlIHRyeWluZyB0byB1cGxvYWQgYSBiaWcgZmlsZSAoPiAxMDBNQiksIHlvdSBtYXkgd2FudCB0byBzZXQgbXVsdGlwYXJ0ID0gVFJVRSB3aXRoaW4gdGhlIHB1dF9vYmplY3QoKSBmdW5jdGlvbi4NCg0KTm93IGxldOKAmXMgbGlzdCBhbGwgdGhlIG9iamVjdHMgaW4gb3VyIGJ1Y2tldCB1c2luZyBnZXRfYnVja2V0KCkuDQoNCmBgYHtyfQ0KZ2V0X2J1Y2tldChidWNrZXQgPSAiY3VzdG9tZXJzZWdtZW50YXRpb25kYXRhIikNCmBgYA0KDQpUaGlzIHJldHVybnMgYSBsaXN0IG9mIHMzX29iamVjdHMuIEkgbGlrZSB1c2luZyB0aGUgcmJpbmRsaXN0KCkgZnVuY3Rpb24gZnJvbSB0aGUgZGF0YS50YWJsZSBwYWNrYWdlIHRvIGNvbGxhcHNlIHRoZXNlIG9iamVjdHMgaW50byBhIG5pY2UgYW5kIGNsZWFuIGRhdGEudGFibGUgKGkuZS4gZGF0YS5mcmFtZSkuDQoNCmBgYHtyfQ0KI2RhdGEudGFibGU6OnJiaW5kbGlzdChnZXRfYnVja2V0KGJ1Y2tldCA9ICJjdXN0b21lcnNlZ2VudGF0aW9uZGF0YSIpKQ0KYGBgDQoNCioqV2UgY2FuIGxvYWQgb25lIG9mIHRoZXNlIENTViBmaWxlcyBmcm9tIFMzIGludG8gUiB3aXRoIHRoZSBzM3JlYWRfdXNpbmcoKSBmdW5jdGlvbi4gSGVyZSBhcmUgdGhyZWUgZGlmZmVyZW50IHdheXMgdG8gZG8gdGhpcy4uKioNCg0KDQpgYGB7cn0NCmRhdGEgPSBzM3JlYWRfdXNpbmcoRlVOID0gcmVhZC5jc3YsIG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIpICMgdXNlIHRoZSBzMyBVUkkNCg0KI3MzcmVhZF91c2luZyhGVU4gPSByZWFkLmNzdiwgYnVja2V0ID0gImN1c3RvbWVyc2VnbWVudGF0aW9uZGF0YSIsIG9iamVjdCA9ICJkYXRhLmNzdiIpICAgICMgYnVja2V0IGFuZCBvYmplY3Qgc3BlY2lmaWVkIHNlcGFyYXRlbHkNCiNzM3JlYWRfdXNpbmcoRlVOID0gZGF0YS50YWJsZTo6ZnJlYWQsIG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIpICAjIHVzZSBkYXRhLnRhYmxlJ3MgZnJlYWQoKSBmdW5jdGlvbiBmb3IgZmFzdCBDU1YgcmVhZGluZw0KDQpgYGANCg0KYGBge3J9DQpkYXRhDQpgYGANCg0KIyBmZXRjaGluZyBhbmQgU3RvcmluZyBkYXRhLmNzdiBpbnRvIHRlbXAgZmlsZSANCioqTm90ZSB0aGF0IHRoZSBzZWNvbmQgYW5kIHRoaXJkIGV4YW1wbGVzIHVzZSB0aGUgb2JqZWN04oCZcyBVUkkgd2hpY2ggaXMgZ2l2ZW4gYXMgczM6Ly88YnVja2V0LW5hbWU+LzxrZXk+LkFsdGVybmF0aXZlbHksIHlvdSBjb3VsZCBkb3dubG9hZCB0aGVzZSBmaWxlcyB3aXRoIHNhdmVfb2JqZWN0KCkgYW5kIHRoZW4gcmVhZCB0aGVtIGZyb20gZGlzYyoqDQoNCmBgYHtyfQ0KI3RlbXBmaWxlIDwtIHRlbXBmaWxlKCkgICMgdGVtcCBmaWxlcGF0aCBsaWtlIA0KI3NhdmVfb2JqZWN0KG9iamVjdCA9ICJzMzovL2N1c3RvbWVyc2VnbWVudGF0aW9uZGF0YS9kYXRhLmNzdiIsIGZpbGUgPSB0ZW1wZmlsZSkNCiNyZWFkLmNzdih0ZW1wZmlsZSkNCg0KYGBgDQoNCiMgYmFzaWMgY2hlY2tzIDogDQoNCmBgYHtyfQ0KZGYgPC0gZGF0YS5mcmFtZShkYXRhKQ0KYGBgDQpHZXQgdGhlIHN0cnVjdHVyZSAgYW5kIHN1bW1hcnkgb2YgdGhlIGRhdGEgZnJhbWUuDQpgYGB7cn0NCnR5cGVvZihkZikNCnN0cihkZikNCnN1bW1hcnkoZGYpDQpgYGANCg0KUXVhbnRpdHkgaXMgZ3JlYXRlciB0aGFuIDEwIA0KDQpgYGB7cn0NCmRmW2RmJFF1YW50aXR5ID4xMCxdDQpgYGANCg0K
>>>>>>> a66515b289cc787723b310520693b95cd6ad65ed